﻿using System;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Wolfpack.Core;
using Wolfpack.Core.Checks;
using Wolfpack.Core.Interfaces.Entities;
using Wolfpack.Core.Notification;

namespace Wolfpack.Contrib.Checks.Smtp
{
    public class SmtpPingCheck : HealthCheckBase<SmtpPingCheckConfig>
    {
        /// <summary>
        /// default ctor
        /// </summary>
        public SmtpPingCheck(SmtpPingCheckConfig config)
            : base(config)
        {
        }

        public override void Initialise()
        {
            Logger.Debug("Initialising SmtpPingCheck check for these servers...");
            _config.Servers.ForEach(server => Logger.Debug("\t{0}", server));
        }

        public override void Execute()
        {
            Logger.Debug("SmtpPingCheck is pinging servers...");
            _config.Servers.ToList().ForEach(server =>
            {
                var ascii = Encoding.ASCII;
                var byteGet = ascii.GetBytes("HELO");
                var recvBytes = new Byte[256];

                String msg = null;

                try
                {
                    // Define those variables to be evaluated in the next for loop and  
                    // then used to connect to the server. These variables are defined 
                    // outside the for loop to make them accessible there after.
                    const int port = 25;

                    // Get DNS host information.
                    var hostInfo = Dns.GetHostEntry(server);
                    var ipAddresses = hostInfo.AddressList;

                    var outcome = true;

                    // Evaluate the socket and receiving host IPAddress and IPEndPoint.  
                    foreach (var ipAddress in ipAddresses)
                    {
                        var hostEndPoint = new IPEndPoint(ipAddress, port);

                        // Creates the Socket to send data over a TCP connection.
                        using (var s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                        {

                            // Connect to the host using its IPEndPoint.
                            s.Connect(hostEndPoint);

                            if (!s.Connected)
                            {
                                // Connection failed, try next IPaddress.
                                msg = string.Format("Unable to connect to host {0}", server);
                                outcome = false;
                                continue;
                            }

                            msg = string.Format("Connected to host {0} - ", server);

                            // Sent the HELO request to the host.
                            s.Send(byteGet, byteGet.Length, 0);
                            var bytes = s.Receive(recvBytes, recvBytes.Length, 0);

                            msg = msg + ascii.GetString(recvBytes, 0, bytes);

                            s.Shutdown(SocketShutdown.Both);
                            s.Disconnect(false);
                        }

                        // Found a good connection, exit loop
                        break;
                    }

                    Logger.Debug(msg);

                    var result = HealthCheckData.For(Identity, msg)
                        .ResultIs(outcome)
                        .AddProperty("Server", server);

                    Publish(NotificationRequestBuilder.For(_config.NotificationMode, 
                        result, 
                        UseServerNameAsKey)
                        .Build());
                }
                catch (SocketException e)
                {
                    msg = string.Format("Error pinging host {0}: {1} ", server, e.Message);
                    Logger.Debug(msg);

                    var result = HealthCheckData.For(Identity, msg)
                        .Failed()
                        .AddProperty("Server", server);

                    Publish(NotificationRequestBuilder.For(_config.NotificationMode,
                        result,
                        UseServerNameAsKey)
                        .Build());
                }
            });
        }

        private void UseServerNameAsKey(NotificationRequest notificationRequest)
        {
            notificationRequest.DataKeyGenerator = msg => string.Format("{0}_{1}", _config.FriendlyId,
                notificationRequest.Notification.Properties["Server"]);
        }

        protected override PluginDescriptor BuildIdentity()
        {
            return new PluginDescriptor
            {
                Description = "Pings a mail server and reports SMTP HELO response",
                TypeId = new Guid("95AC7F83-4909-49F4-8207-9CDF5AAD1421"),
                Name = _config.FriendlyId
            };
        }
    }
}
